Load libraries and data

root.dir <- "~/OneDrive/projects/"
source(paste0(root.dir,"R.utils/RNASEQ.utils.R"))
project.dir <- paste0(root.dir,"PDX/")
source(paste0(project.dir,"src/load.PDX.R"))
rnaseq <- get.PDX()

Read 0.0% of 113262 rows
Read 44.1% of 113262 rows
Read 88.3% of 113262 rows
Read 113262 rows and 749 (of 749) columns from 0.176 GB file in 00:00:06
meta <- load.meta()
meta <- subset.meta(meta,meta[,project=="metastasis" & patient=="AB861"])
rnaseq <- subset.PDX(rnaseq, rnaseq$meta[,Sample.name] %in% meta[,Sample.name])
hpa.breast <- fread(paste0(root.dir,"shared.data/human.protein.atlas/tissue_specificity_rna_breast.tsv"),check.names = T)
hpa.lymph <- fread(paste0(root.dir,"shared.data/human.protein.atlas/tissue_specificity_rna_lymph.tsv"),check.names = T)
hpa.gene.list <- hpa.lymph[!(Gene %in% hpa.breast$Gene),.(Gene,Ensembl)]

QC

read.depth.plots(rnaseq$meta)

There appears to be some variation across the samples as to the mouse fraction. Does this correlate to the passage number?

x <- melt(rnaseq$meta,id.vars=c("Sample.name","Flowcell","Lane","Pool"),measure.vars = c("mouse.library.size","human.library.size"))
x <- merge(x,meta)[,.(tissue.grafted,tissue.sampled,variable,value/sum(value)),Sample.name][variable=="mouse.library.size"]
ggplot(x) + aes(x=tissue.grafted,y=V4,colour=tissue.sampled) + geom_boxplot() + theme(axis.text.x=element_text(angle=90,hjust=1))

While x4 samples do have a larger mouse fraction than others, the x3 samples have less than either the x2 or x1 samples indicating that it isn’t a clear trend. Also the samples taken from the patient show little to no mouse fraction as would be expected (a good sanity check).

No big issues –> proceeding to merge lanes and meta

rnaseq <- merge.PDX(rnaseq)
rnaseq$meta <- merge(rnaseq$meta,meta,sort=F)
rnaseq$meta[,merged.lanes:=abbreviate(merged.lanes)]

Key questions

  • Are there any expression differences between the patient’s primary and met samples?
  • Within model AB861M-X1T, are there any expression differences between the tumour and the lymph nodes mets?
  • Within model AB861M-X1T, are there any expression differences between the tumour fragments that did and did not metastasise? (see Abby)
  • Are there any expression differences between the tumours, as the models have been passaged and metastatic populations have been selected? E.g. compare tumours from AB861M-X1T, AB861X1M-X0T, AB861X1M1-X0T

PCAs

Before we tackle the above, let’s perform a few quick pca’s.

pca <- pca.run(rnaseq$human)
summary(pca)
Importance of components:
                           PC1     PC2     PC3     PC4     PC5     PC6     PC7     PC8     PC9    PC10    PC11    PC12    PC13   PC14    PC15    PC16
Standard deviation     26.3071 12.4314 8.02369 4.04841 3.19778 3.04445 2.52645 2.52160 2.35886 2.07895 1.86536 1.75551 1.58955 1.5159 1.42245 1.39556
Proportion of Variance  0.6921  0.1545 0.06438 0.01639 0.01023 0.00927 0.00638 0.00636 0.00556 0.00432 0.00348 0.00308 0.00253 0.0023 0.00202 0.00195
Cumulative Proportion   0.6921  0.8466 0.91098 0.92737 0.93760 0.94687 0.95325 0.95961 0.96517 0.96950 0.97298 0.97606 0.97858 0.9809 0.98291 0.98485
                          PC17    PC18    PC19    PC20    PC21    PC22    PC23    PC24    PC25    PC26    PC27    PC28    PC29    PC30    PC31    PC32
Standard deviation     1.31009 1.27388 1.24328 1.13470 1.07615 1.05624 1.03049 0.93928 0.89966 0.88765 0.87188 0.83051 0.79480 0.72142 0.56480 0.48835
Proportion of Variance 0.00172 0.00162 0.00155 0.00129 0.00116 0.00112 0.00106 0.00088 0.00081 0.00079 0.00076 0.00069 0.00063 0.00052 0.00032 0.00024
Cumulative Proportion  0.98657 0.98819 0.98974 0.99103 0.99218 0.99330 0.99436 0.99524 0.99605 0.99684 0.99760 0.99829 0.99892 0.99944 0.99976 1.00000
                            PC33
Standard deviation     1.168e-14
Proportion of Variance 0.000e+00
Cumulative Proportion  1.000e+00
pca <- cbind(rnaseq$meta,pca$x)
ggplot(pca) + aes(x=PC1,y=PC2,col=tissue.sampled,shape=xenograft) + geom_point(size=5)

The human samples are clearly very differet to the rest. However, what is causing the grouping between the xenograft samples?

x <- subset.PDX(rnaseq,rnaseq$meta[,xenograft])
pca <- pca.run(x$human)
summary(pca)
Importance of components:
                           PC1     PC2     PC3     PC4     PC5     PC6     PC7    PC8     PC9    PC10    PC11   PC12   PC13    PC14    PC15    PC16
Standard deviation     24.9891 9.29872 6.68410 6.12936 5.78557 5.16967 4.66610 3.9367 3.84352 3.44039 2.90136 2.8459 2.7384 2.57268 2.53240 2.48157
Proportion of Variance  0.6244 0.08647 0.04468 0.03757 0.03347 0.02673 0.02177 0.0155 0.01477 0.01184 0.00842 0.0081 0.0075 0.00662 0.00641 0.00616
Cumulative Proportion   0.6244 0.71092 0.75560 0.79317 0.82664 0.85336 0.87514 0.8906 0.90541 0.91724 0.92566 0.9338 0.9413 0.94788 0.95429 0.96045
                          PC17    PC18    PC19    PC20    PC21   PC22    PC23    PC24   PC25    PC26    PC27    PC28    PC29     PC30
Standard deviation     2.25566 2.23810 2.11002 1.96986 1.84778 1.7883 1.73908 1.64565 1.5152 1.47298 1.43199 1.09049 1.03512 1.11e-14
Proportion of Variance 0.00509 0.00501 0.00445 0.00388 0.00341 0.0032 0.00302 0.00271 0.0023 0.00217 0.00205 0.00119 0.00107 0.00e+00
Cumulative Proportion  0.96554 0.97055 0.97500 0.97888 0.98229 0.9855 0.98852 0.99122 0.9935 0.99569 0.99774 0.99893 1.00000 1.00e+00
pca <- cbind(x$meta,pca$x)
ggplot(pca) + aes(x=PC1,y=PC2,col=tissue.grafted,shape=exp.protocol) + geom_point(size=5)

ggplot(pca) + aes(x=PC1,y=PC2,col=tissue.grafted,shape=tissue.sampled) + geom_point(size=5)

ggplot(pca) + aes(x=PC1,y=PC2,col=tissue.grafted,shape=merged.lanes) + geom_point(size=5)

We note that there is clustering based on the engrafted tissue and the experimental protocol. However, since these latter clusterings are not independent from one another, we cannot discern if the major transcriptomic change after the initial two passages is caused by changes in the tumor (tissue.grafted) or the changed protocol. Additional samples should be sequenced to try discern this. Note that this point in the sequential passages is the first time a mouse met is reimplanted.

Model: ~ 0 + tissue.grafted

Between generations (Mets only)

x <- subset.PDX(rnaseq,rnaseq$meta[,xenograft & !(tissue.grafted %in% c("AB861M","AB861M-XT1-XM1-XT1")) & tissue.sampled=="LN"])
tissue.grafted <- make.names(x$meta$tissue.grafted)
d <- model.matrix(~ 0 + tissue.grafted)
cont.matrix <- makeContrasts(
   G2vsG1 = tissue.graftedAB861M.XT1.XM1 - tissue.graftedAB861M.XT1,
   G3vsG2 = tissue.graftedAB861M.XT1.XM1.XM1 - tissue.graftedAB861M.XT1.XM1,
   levels = colnames(d))
#Perform DE
DE.data <- DE.run(x$human,d,2,cont.matrix = cont.matrix)

DE <- decideTest.annotated.DE(DE.data$efit)
'select()' returned 1:many mapping between keys and columns
vennDiagram.paired(DE[,2:3])

DE <- DE[abs(rowSums(DE[,2:3]))>=2]
DE
#Pheatmap w/ batch correction
i <- match(DE$ENSEMBL,rownames(DE.data$v$E))
labels.col <- rep("",nrow(x$meta))
ann.col <- data.frame(Generation=x$meta[,tissue.grafted])
labels.row <- DE$SYMBOL
rownames(ann.col) <- x$meta[,Sample.name]
levels(ann.col$Generation) <- c("G1","G2","G3")
j <- order(ann.col$Generation)
cols <- hue_pal()(4)[1:3]
names(cols) <- levels(ann.col$Generation)
pheatmap(DE.data$v$E[i,j],labels_col=labels.col,labels_row=labels.row,annotation_col = ann.col[j,,drop=F],scale="row",cluster_cols = F,annotation_colors = list(Generation=cols))

#Pheatmap with tissue.graftedAB861M.XT1.XM1.XT1 added
y <- subset.PDX(rnaseq,rnaseq$meta[,xenograft & !(tissue.grafted %in% c("AB861M")) & tissue.sampled=="LN"])
z <- DGEList(y$human[rowSums(cpm(y$human) > cpm.threshold(y$human)) >= 2,])
z <- calcNormFactors(z,method="TMM")
z <- voom(z,y$meta[,model.matrix(~ 0 + tissue.grafted)])
i <- match(DE$ENSEMBL,rownames(z))
labels.col <- rep("",nrow(y$meta))
ann.col <- data.frame(Generation=y$meta[,tissue.grafted])
labels.row <- DE$SYMBOL
rownames(ann.col) <- y$meta[,Sample.name]
levels(ann.col$Generation) <- c("G1","G2","G3","G2x1")
cols <- hue_pal()(4)
names(cols) <- levels(ann.col$Generation)
pheatmap(z[i,],labels_col=labels.col,labels_row=labels.row,annotation_col = ann.col,scale="row",annotation_colors = list(Generation=cols))

Between generations (Flank only)

x <- subset.PDX(rnaseq,rnaseq$meta[,xenograft & !(tissue.grafted %in% c("AB861M","AB861M-XT1-XM1-XT1")) & tissue.sampled=="T"])
tissue.grafted <- make.names(x$meta$tissue.grafted)
d <- model.matrix(~ 0 + tissue.grafted)
cont.matrix <- makeContrasts(
   G2vsG1 = tissue.graftedAB861M.XT1.XM1 - tissue.graftedAB861M.XT1,
   G3vsG2 = tissue.graftedAB861M.XT1.XM1.XM1 - tissue.graftedAB861M.XT1.XM1,
   levels = colnames(d))
#Perform DE
DE.data <- DE.run(x$human,d,2,cont.matrix = cont.matrix)

DE <- decideTest.annotated.DE(DE.data$efit)
'select()' returned 1:many mapping between keys and columns
vennDiagram.paired(DE[,2:3])

DE <- DE[abs(rowSums(DE[,2:3]))==2]
DE
#Pheatmap w/ batch correction
i <- match(DE$ENSEMBL,rownames(DE.data$v$E))
labels.col <- rep("",nrow(x$meta))
ann.col <- data.frame(Generation=x$meta[,tissue.grafted])
labels.row <- DE$SYMBOL
rownames(ann.col) <- x$meta[,Sample.name]
levels(ann.col$Generation) <- c("G1","G2","G3")
j <- order(ann.col$Generation)
cols <- hue_pal()(4)[1:3]
names(cols) <- levels(ann.col$Generation)
pheatmap(DE.data$v$E[i,j],labels_col=labels.col,labels_row=labels.row,annotation_col = ann.col[j,,drop=F],scale="row",cluster_cols = F,annotation_colors = list(Generation=cols))

#Pheatmap with tissue.graftedAB861M.XT1.XM1.XT1 added
y <- subset.PDX(rnaseq,rnaseq$meta[,xenograft & !(tissue.grafted %in% c("AB861M")) & tissue.sampled=="T"])
z <- DGEList(y$human[rowSums(cpm(y$human) > cpm.threshold(y$human)) >= 2,])
z <- calcNormFactors(z,method="TMM")
z <- voom(z,y$meta[,model.matrix(~ 0 + tissue.grafted)])
i <- match(DE$ENSEMBL,rownames(z))
labels.col <- rep("",nrow(y$meta))
ann.col <- data.frame(Generation=y$meta[,tissue.grafted])
labels.row <- DE$SYMBOL
rownames(ann.col) <- y$meta[,Sample.name]
levels(ann.col$Generation) <- c("G1","G2","G3","G2x1")
cols <- hue_pal()(4)
names(cols) <- levels(ann.col$Generation)
pheatmap(z[i,],labels_col=labels.col,labels_row=labels.row,annotation_col = ann.col,scale="row",annotation_colors = list(Generation=cols))

Model: ~ 0 + tissue.sampled + tissue.grafted + (batch)

Flank vs Lymph

x <- subset.PDX(rnaseq,rnaseq$meta[,xenograft & tissue.grafted!="AB861M"])
tissue.sampled <- factor(make.names(x$meta$tissue.sampled),levels = c("T","LN"))
tissue.grafted <- make.names(x$meta$tissue.grafted)
mouse.id <- make.names(x$meta$mouse)
exp.protocol <- make.names(x$meta$exp.protocol)
merged.lanes <- make.names(x$meta$merged.lanes)
d <- model.matrix(~ 0 + tissue.sampled + tissue.grafted + mouse.id)
d <- d[,!(colnames(d) %in% c("mouse.idX45241","mouse.idX48803","mouse.idX48898"))]
cont.matrix <- makeContrasts(
   LNvsT = tissue.sampledLN - tissue.sampledT,
   levels = colnames(d))
rownames(cont.matrix) <- colnames(d)
#Perform DE
DE.data <- DE.run(x$human,d,2,cont.matrix = cont.matrix)

#Top DE genes table
DE <- topTable.annotated.DE(DE.data$efit,p.val = 0.01)
'select()' returned 1:1 mapping between keys and columns
DE <- DE[!(ENSEMBL %in% hpa.gene.list$Ensembl)]
if(nrow(DE)!=0){
  print(DE[direction=="up.reg",.(direction,ENSEMBL,SYMBOL,adj.P.Val,logFC)])
  print(DE[direction=="down.reg",.(direction,ENSEMBL,SYMBOL,adj.P.Val,logFC)])
#Pheatmap w/ batch correction
  i <- match(DE$ENSEMBL,rownames(DE.data$v$E))
  y <- removeBatchEffect(DE.data$v$E[i,],design = model.matrix(~tissue.sampled), batch= tissue.grafted)
  labels.col <- rep("",nrow(x$meta))
  labels.row <- DE$SYMBOL
  ann.col <- as.data.frame(x$meta[,.(tissue.sampled,tissue.grafted)])
  rownames(ann.col) <- x$meta[,Sample.name]
  pheatmap(y,labels_col=labels.col,labels_row=labels.row,annotation_col = ann.col,scale="row")
  
  #Pheatmap w/o batch correction
  i <- match(DE$ENSEMBL,rownames(DE.data$v$E))
  y <- DE.data$v$E[i,]
  labels.col <- rep("",nrow(x$meta))
  labels.row <- DE$SYMBOL
  ann.col <- as.data.frame(x$meta[,.(tissue.sampled,tissue.grafted)])
  rownames(ann.col) <- x$meta[,Sample.name]
  pheatmap(y,labels_col=labels.col,labels_row=labels.row,annotation_col = ann.col,scale="row")
}

#GSEA - c6
gsea <- GSEA.run(DE.data$v,d,cont.matrix,Hs.c6,0.05)
'select()' returned 1:many mapping between keys and columns
gsea[Direction=="Up",.(rn,NGenes,Direction,FDR)]
gsea[Direction=="Down",.(rn,NGenes,Direction,FDR)]
#GSEA - c2
gsea <- GSEA.run(DE.data$v,d,cont.matrix,Hs.c2,0.05)
'select()' returned 1:many mapping between keys and columns
gsea[Direction=="Up",.(rn,NGenes,Direction,FDR)]
gsea[Direction=="Down",.(rn,NGenes,Direction,FDR)]
# ssGSEA
ssGSEA.data <- ssGSEA.run(DE.data$v$E,Hs.c2,d,cont.matrix = cont.matrix)
'select()' returned 1:many mapping between keys and columns
The argument 'rnaseq' is deprecated and will be removed in the next release of GSVA. Please use the 'kcdf' argument instead.
Estimating GSVA scores for 4472 gene sets.
Computing observed enrichment scores
Estimating ECDFs in microarray data with Gaussian kernels
Using parallel with 4 cores

  |                                                                                                                                                   
  |                                                                                                                                             |   0%

#Top DE gene.sets table
ssGSEA <- topTable.annotated.ssGSEA(ssGSEA.data$efit)
if(nrow(ssGSEA)!=0){
  print(ssGSEA[direction=="up.reg",.(direction,gene.set,adj.P.Val,logFC)])
  print(ssGSEA[direction=="down.reg",.(direction,gene.set,adj.P.Val,logFC)])
  
  #Pheatmap w/ batch correction
  i <- match(ssGSEA$gene.set,rownames(ssGSEA.data$w))
  y <- removeBatchEffect(ssGSEA.data$w[i,],design = model.matrix(~tissue.sampled), batch= tissue.grafted)
  labels.col <- rep("",nrow(rnaseq$meta))
  labels.row <- ssGSEA$gene.set
  ann.col <- as.data.frame(rnaseq$meta[,.(tissue.sampled,tissue.grafted)])
  rownames(ann.col) <- rnaseq$meta[,Sample.name]
  pheatmap(y,labels_col=labels.col,labels_row=labels.row,annotation_col = ann.col,scale="row")
}

Between generations

d <- model.matrix(~ 0 + tissue.grafted + tissue.sampled + mouse.id)
d <- d[,!(colnames(d) %in% c("mouse.idX45241","mouse.idX48803","mouse.idX48898"))]
cont.matrix <- makeContrasts(
   G2vsG1 = tissue.graftedAB861M.XT1.XM1 - tissue.graftedAB861M.XT1,
   G3vsG2 = tissue.graftedAB861M.XT1.XM1.XM1 - tissue.graftedAB861M.XT1.XM1,
   levels = colnames(d))
rownames(cont.matrix) <- colnames(d)
vfit <- lmFit(DE.data$v, d)
vfit <- contrasts.fit(vfit, contrasts = cont.matrix)
efit <- eBayes(vfit)
DE.sub <- decideTest.annotated.DE(efit)
'select()' returned 1:many mapping between keys and columns
table(DE.sub[,2:3])
      G3vsG2
G2vsG1  -1   0   1
    -1   1 229  23
    0   74   0 240
    1    0  30   2
vennDiagram.paired(DE.sub[,2:3])

DE.sub[abs(rowSums(DE.sub[,2:3]))==2,]

Between generations (only F vs LN DE genes)

i <- match(DE$ENSEMBL,rownames(DE.data$v$E))
vfit <- lmFit(DE.data$v[i,], d)
vfit <- contrasts.fit(vfit, contrasts = cont.matrix)
efit <- eBayes(vfit)
DE.sub <- decideTest.annotated.DE(efit)
'select()' returned 1:1 mapping between keys and columns
table(DE.sub[,2:3])
      G3vsG2
G2vsG1 -1  0  1
    -1  0 18  3
    0   1  0  2
vennDiagram.paired(DE.sub[,2:3])

DE.sub[abs(rowSums(DE.sub[,2:3]))==2,]

Human data

y <- subset.PDX(rnaseq,rnaseq$meta[,is.na(tissue.grafted)])$human
y <- log(t(1e6*t(y) / (colSums(y)*calcNormFactors(y))+0.5))
up.reg <- apply(y[rownames(y) %in% DE[direction=="up.reg",ENSEMBL],],1,function(y)y[3]-y[2])
down.reg <- apply(y[rownames(y) %in% DE[direction=="down.reg",ENSEMBL],],1,function(y)y[3]-y[2])
wilcox.test(up.reg,down.reg,alternative = "g")

    Wilcoxon rank sum test with continuity correction

data:  up.reg and down.reg
W = 500, p-value = 0.06827
alternative hypothesis: true location shift is greater than 0
z <- rbind(data.table(direction="up",LFC=up.reg,ENSEMBL=names(up.reg)),
           data.table(direction="down",LFC=down.reg,ENSEMBL=names(down.reg)))
ggplot(z) + aes(x=LFC,fill=direction) + geom_density(alpha=0.5)

z <- merge(z,DE,by="ENSEMBL")
table(z[,.(direction.x,direction.human=LFC>0)])
           direction.human
direction.x FALSE TRUE
       down    34   16
       up       8    8
z[direction.x=="up",.(ENSEMBL,SYMBOL,LFC,direction.x,LFC>0)][order(-LFC)]
z[direction.x=="down",.(ENSEMBL,SYMBOL,LFC,direction.x,LFC>0)][order(LFC)]

Model: ~ 0 + tissue.sampled*tissue.grafted + (batch)

Flank vs Lymph

x <- subset.PDX(rnaseq,rnaseq$meta[,xenograft & tissue.grafted!="AB861M"])
tissue.sampled <- factor(make.names(x$meta$tissue.sampled),levels = c("T","LN"))
tissue.grafted <- make.names(x$meta$tissue.grafted)
mouse.id <- make.names(x$meta$mouse)
exp.protocol <- make.names(x$meta$exp.protocol)
merged.lanes <- make.names(x$meta$merged.lanes)
d <- model.matrix(~ 0 + tissue.sampled * tissue.grafted + mouse.id + merged.lanes)
d <- d[,!(colnames(d) %in% c("mouse.idX45241","mouse.idX48803","mouse.idX48898"))]
cont.matrix <-  matrix(c(-1,1,rep(0,dim(d)[2]-2)))
rownames(cont.matrix) <- colnames(d)
#Perform DE
DE.data <- DE.run(x$human,d,2,cont.matrix = cont.matrix)

#Top DE genes table
DE <- topTable.annotated.DE(DE.data$efit,p.val = 0.05)
'select()' returned 1:1 mapping between keys and columns
DE <- DE[!(ENSEMBL %in% hpa.gene.list$Ensembl)]
if(nrow(DE)!=0){
  print(DE[direction=="up.reg",.(direction,ENSEMBL,SYMBOL,adj.P.Val,logFC)])
  print(DE[direction=="down.reg",.(direction,ENSEMBL,SYMBOL,adj.P.Val,logFC)])
#Pheatmap w/ batch correction
  #i <- match(DE$ENSEMBL,rownames(DE.data$v$E))
  #y <- removeBatchEffect(DE.data$v$E[i,],design = model.matrix(~tissue.sampled), batch= tissue.grafted)
  #labels.row <- DE$SYMBOL
  #labels.col <- rep("",nrow(x$meta))
  #ann.col <- as.data.frame(x$meta[,.(tissue.sampled,tissue.grafted)])
  #rownames(ann.col) <- x$meta[,Sample.name]
  #pheatmap(y,labels_col=labels.col,labels_row=labels.row,annotation_col = ann.col,scale="row")
}
#GSEA - c6
gsea <- GSEA.run(DE.data$v,d,cont.matrix,Hs.c6,0.05)
'select()' returned 1:many mapping between keys and columns
gsea[Direction=="Up",.(rn,NGenes,Direction,FDR)]
gsea[Direction=="Down",.(rn,NGenes,Direction,FDR)]
#GSEA - c2
gsea <- GSEA.run(DE.data$v,d,cont.matrix,Hs.c2,0.05)
'select()' returned 1:many mapping between keys and columns
gsea[Direction=="Up",.(rn,NGenes,Direction,FDR)]
gsea[Direction=="Down",.(rn,NGenes,Direction,FDR)]
# ssGSEA
ssGSEA.data <- ssGSEA.run(DE.data$v$E,Hs.c2,d,cont.matrix = cont.matrix)
'select()' returned 1:many mapping between keys and columns
The argument 'rnaseq' is deprecated and will be removed in the next release of GSVA. Please use the 'kcdf' argument instead.
Estimating GSVA scores for 4472 gene sets.
Computing observed enrichment scores
Estimating ECDFs in microarray data with Gaussian kernels
Using parallel with 4 cores

  |                                                                                                                                                   
  |                                                                                                                                             |   0%

#Top DE gene.sets table
ssGSEA <- topTable.annotated.ssGSEA(ssGSEA.data$efit)
No DE genes
if(nrow(ssGSEA)!=0){
  print(ssGSEA[direction=="up.reg",.(direction,gene.set,adj.P.Val,logFC)])
  print(ssGSEA[direction=="down.reg",.(direction,gene.set,adj.P.Val,logFC)])
  
  #Pheatmap w/ batch correction
  i <- match(ssGSEA$gene.set,rownames(ssGSEA.data$w))
  y <- removeBatchEffect(ssGSEA.data$w[i,],design = model.matrix(~tissue.sampled), batch= tissue.grafted)
  labels.col <- rep("",nrow(rnaseq$meta))
  labels.row <- ssGSEA$gene.set
  ann.col <- as.data.frame(rnaseq$meta[,.(tissue.sampled,tissue.grafted)])
  rownames(ann.col) <- rnaseq$meta[,Sample.name]
  pheatmap(y,labels_col=labels.col,labels_row=labels.row,annotation_col = ann.col,scale="row")
}

Model: ~ tissue.sampled:tissue.grafted + (batch)

Note: only using paired samples

Flank vs Lymph

x <- subset.PDX(rnaseq,rnaseq$meta[,xenograft & mouse %in% rnaseq$meta[,.N,mouse][N==2,mouse]])
tissue.sampled <- factor(make.names(x$meta$tissue.sampled),levels = c("T","LN"))
tissue.grafted <- make.names(x$meta$tissue.grafted)
mouse.id <- make.names(x$meta$mouse)
exp.protocol <- make.names(x$meta$exp.protocol)
merged.lanes <- make.names(x$meta$merged.lanes)
d <- model.matrix(~ tissue.sampled:tissue.grafted + mouse.id)
d <- d[,!(colnames(d) %in% c("tissue.sampledT:tissue.graftedAB861M.XT1","tissue.sampledT:tissue.graftedAB861M.XT1.XM1","tissue.sampledT:tissue.graftedAB861M.XT1.XM1.XM1","tissue.sampledT:tissue.graftedAB861M.XT1.XM1.XT1"))]
DE.data <- DE.run(x$human,d,2,coefs = 12:15)

DE <- decideTest.annotated.DE(DE.data$efit)
'select()' returned 1:1 mapping between keys and columns
vennDiagram.paired(DE[,2:5])

Mice with mets vs mice without mets (AB861M-XT1)

DE: human reads

x <- subset.PDX(rnaseq,rnaseq$meta[,xenograft & tissue.grafted=="AB861M-XT1" & tissue.sampled=="T"])
x$meta[,mets:=!(mouse %in% c(35309,35311,35311,35312,36361))]
mets <- make.names(x$meta$mets)
d <- model.matrix(~ mets)
DE.data <- DE.run(x$human,d,2,coefs = 2)

#Top DE genes table
DE <- topTable.annotated.DE(DE.data$efit,p.val = 0.05)
No DE genes
if(nrow(DE)!=0){
  DE <- DE[!(ENSEMBL %in% hpa.gene.list$Ensembl)]
  print(DE[direction=="up.reg",.(direction,ENSEMBL,SYMBOL,adj.P.Val,logFC)])
  print(DE[direction=="down.reg",.(direction,ENSEMBL,SYMBOL,adj.P.Val,logFC)])
#Pheatmap w/ batch correction
  i <- match(DE$ENSEMBL,rownames(DE.data$v$E))
  y <- removeBatchEffect(DE.data$v$E[i,],design = model.matrix(~tissue.sampled), batch= tissue.grafted)
  labels.row <- DE$SYMBOL
  labels.col <- rep("",nrow(x$meta))
  ann.col <- as.data.frame(x$meta[,.(tissue.sampled,tissue.grafted)])
  rownames(ann.col) <- x$meta[,Sample.name]
  pheatmap(y,labels_col=labels.col,labels_row=labels.row,annotation_col = ann.col,scale="row")
}
#GSEA - c6
gsea <- GSEA.run(DE.data$v,d,2,Hs.c6,0.05)
'select()' returned 1:many mapping between keys and columns
gsea[Direction=="Up",.(rn,NGenes,Direction,FDR)]
gsea[Direction=="Down",.(rn,NGenes,Direction,FDR)]
#GSEA - c2
gsea <- GSEA.run(DE.data$v,d,2,Hs.c2,0.05)
'select()' returned 1:many mapping between keys and columns
gsea[Direction=="Up",.(rn,NGenes,Direction,FDR)]
gsea[Direction=="Down",.(rn,NGenes,Direction,FDR)]
# ssGSEA
ssGSEA.data <- ssGSEA.run(DE.data$v$E,Hs.c2,d,coefs = 2)
'select()' returned 1:many mapping between keys and columns
The argument 'rnaseq' is deprecated and will be removed in the next release of GSVA. Please use the 'kcdf' argument instead.
Estimating GSVA scores for 4433 gene sets.
Computing observed enrichment scores
Estimating ECDFs in microarray data with Gaussian kernels
Using parallel with 4 cores

  |                                                                                                                                                   
  |                                                                                                                                             |   0%

#Top DE gene.sets table
ssGSEA <- topTable.annotated.ssGSEA(ssGSEA.data$efit)
No DE genes
if(nrow(ssGSEA)!=0){
  print(ssGSEA[direction=="up.reg",.(direction,gene.set,adj.P.Val,logFC)])
  print(ssGSEA[direction=="down.reg",.(direction,gene.set,adj.P.Val,logFC)])
  
  #Pheatmap w/ batch correction
  i <- match(ssGSEA$gene.set,rownames(ssGSEA.data$w))
  y <- removeBatchEffect(ssGSEA.data$w[i,],design = model.matrix(~tissue.sampled), batch= tissue.grafted)
  labels.col <- rep("",nrow(rnaseq$meta))
  labels.row <- ssGSEA$gene.set
  ann.col <- as.data.frame(rnaseq$meta[,.(tissue.sampled,tissue.grafted)])
  rownames(ann.col) <- rnaseq$meta[,Sample.name]
  pheatmap(y,labels_col=labels.col,labels_row=labels.row,annotation_col = ann.col,scale="row")
}

DE: mouse reads

DE.data <- DE.run(x$mouse,d,2,coefs = 2)

#Top DE genes table
DE <- topTable.annotated.DE(DE.data$efit,p.val = 0.05)
No DE genes
if(nrow(DE)!=0){
  DE <- DE[!(ENSEMBL %in% hpa.gene.list$Ensembl)]
  print(DE[direction=="up.reg",.(direction,ENSEMBL,SYMBOL,adj.P.Val,logFC)])
  print(DE[direction=="down.reg",.(direction,ENSEMBL,SYMBOL,adj.P.Val,logFC)])
}

Session Info

sessionInfo()
R version 3.4.2 (2017-09-28)
Platform: x86_64-apple-darwin16.7.0 (64-bit)
Running under: macOS Sierra 10.12.6

Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib

locale:
[1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8

attached base packages:
[1] parallel  stats4    stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] BiocInstaller_1.28.0 pheatmap_1.0.8       ggthemes_3.4.0       ggplot2_2.2.1        org.Mm.eg.db_3.5.0   org.Hs.eg.db_3.5.0   AnnotationDbi_1.40.0
 [8] IRanges_2.12.0       S4Vectors_0.16.0     Biobase_2.38.0       BiocGenerics_0.24.0  GSVA_1.26.0          edgeR_3.20.1         limma_3.34.1        
[15] data.table_1.10.4-3 

loaded via a namespace (and not attached):
 [1] statmod_1.4.30     locfit_1.5-9.1     reshape2_1.4.2     splines_3.4.2      lattice_0.20-35    colorspace_1.3-2   htmltools_0.3.6   
 [8] yaml_2.1.14        blob_1.1.0         XML_3.98-1.9       rlang_0.1.4        DBI_0.7            bit64_0.9-7        RColorBrewer_1.1-2
[15] plyr_1.8.4         stringr_1.2.0      munsell_0.4.3      gtable_0.2.0       memoise_1.1.0      labeling_0.3       knitr_1.17        
[22] geneplotter_1.56.0 httpuv_1.3.5       GSEABase_1.40.0    Rcpp_0.12.13       xtable_1.8-2       scales_0.5.0       graph_1.56.0      
[29] annotate_1.56.1    mime_0.5           bit_1.1-12         digest_0.6.12      stringi_1.1.5      shiny_1.0.5        grid_3.4.2        
[36] tools_3.4.2        bitops_1.0-6       magrittr_1.5       RCurl_1.95-4.8     lazyeval_0.2.1     shinythemes_1.1.1  tibble_1.3.4      
[43] RSQLite_2.0        pkgconfig_2.0.1    assertthat_0.2.0   R6_2.2.2           compiler_3.4.2    
LS0tCnRpdGxlOiAnUERYOiBNZXQgcHJvamVjdCcKYXV0aG9yOiAiQWxpc3RhaXIgTWFydGluIgpkYXRlOiAnYHIgZm9ybWF0KFN5cy50aW1lKCksICJMYXN0IG1vZGlmaWVkOiAlZCAlYiAlWSIpYCcKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCnN1YnRpdGxlOiBNb2RlbCBBQjg2MQpsYXlvdXQ6IHBhZ2UKLS0tCgojTG9hZCBsaWJyYXJpZXMgYW5kIGRhdGEKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnJvb3QuZGlyIDwtICJ+L09uZURyaXZlL3Byb2plY3RzLyIKc291cmNlKHBhc3RlMChyb290LmRpciwiUi51dGlscy9STkFTRVEudXRpbHMuUiIpKQoKcHJvamVjdC5kaXIgPC0gcGFzdGUwKHJvb3QuZGlyLCJQRFgvIikKc291cmNlKHBhc3RlMChwcm9qZWN0LmRpciwic3JjL2xvYWQuUERYLlIiKSkKcm5hc2VxIDwtIGdldC5QRFgoKQptZXRhIDwtIGxvYWQubWV0YSgpCgptZXRhIDwtIHN1YnNldC5tZXRhKG1ldGEsbWV0YVsscHJvamVjdD09Im1ldGFzdGFzaXMiICYgcGF0aWVudD09IkFCODYxIl0pCnJuYXNlcSA8LSBzdWJzZXQuUERYKHJuYXNlcSwgcm5hc2VxJG1ldGFbLFNhbXBsZS5uYW1lXSAlaW4lIG1ldGFbLFNhbXBsZS5uYW1lXSkKCmhwYS5icmVhc3QgPC0gZnJlYWQocGFzdGUwKHJvb3QuZGlyLCJzaGFyZWQuZGF0YS9odW1hbi5wcm90ZWluLmF0bGFzL3Rpc3N1ZV9zcGVjaWZpY2l0eV9ybmFfYnJlYXN0LnRzdiIpLGNoZWNrLm5hbWVzID0gVCkKaHBhLmx5bXBoIDwtIGZyZWFkKHBhc3RlMChyb290LmRpciwic2hhcmVkLmRhdGEvaHVtYW4ucHJvdGVpbi5hdGxhcy90aXNzdWVfc3BlY2lmaWNpdHlfcm5hX2x5bXBoLnRzdiIpLGNoZWNrLm5hbWVzID0gVCkKaHBhLmdlbmUubGlzdCA8LSBocGEubHltcGhbIShHZW5lICVpbiUgaHBhLmJyZWFzdCRHZW5lKSwuKEdlbmUsRW5zZW1ibCldCmBgYAoKI1FDCgpgYGB7cixmaWcud2lkdGg9MTIsZmlnLmhlaWdodD02fQpyZWFkLmRlcHRoLnBsb3RzKHJuYXNlcSRtZXRhKQpgYGAKClRoZXJlIGFwcGVhcnMgdG8gYmUgc29tZSB2YXJpYXRpb24gYWNyb3NzIHRoZSBzYW1wbGVzIGFzIHRvIHRoZSBtb3VzZSBmcmFjdGlvbi4gRG9lcyB0aGlzIGNvcnJlbGF0ZSB0byB0aGUgcGFzc2FnZSBudW1iZXI/CgpgYGB7cn0KeCA8LSBtZWx0KHJuYXNlcSRtZXRhLGlkLnZhcnM9YygiU2FtcGxlLm5hbWUiLCJGbG93Y2VsbCIsIkxhbmUiLCJQb29sIiksbWVhc3VyZS52YXJzID0gYygibW91c2UubGlicmFyeS5zaXplIiwiaHVtYW4ubGlicmFyeS5zaXplIikpCnggPC0gbWVyZ2UoeCxtZXRhKVssLih0aXNzdWUuZ3JhZnRlZCx0aXNzdWUuc2FtcGxlZCx2YXJpYWJsZSx2YWx1ZS9zdW0odmFsdWUpKSxTYW1wbGUubmFtZV1bdmFyaWFibGU9PSJtb3VzZS5saWJyYXJ5LnNpemUiXQpnZ3Bsb3QoeCkgKyBhZXMoeD10aXNzdWUuZ3JhZnRlZCx5PVY0LGNvbG91cj10aXNzdWUuc2FtcGxlZCkgKyBnZW9tX2JveHBsb3QoKSArIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT05MCxoanVzdD0xKSkKYGBgCgpXaGlsZSB4NCBzYW1wbGVzIGRvIGhhdmUgYSBsYXJnZXIgbW91c2UgZnJhY3Rpb24gdGhhbiBvdGhlcnMsIHRoZSB4MyBzYW1wbGVzIGhhdmUgbGVzcyB0aGFuIGVpdGhlciB0aGUgeDIgb3IgeDEgc2FtcGxlcyBpbmRpY2F0aW5nIHRoYXQgaXQgaXNuJ3QgYSBjbGVhciB0cmVuZC4gQWxzbyB0aGUgc2FtcGxlcyB0YWtlbiBmcm9tIHRoZSBwYXRpZW50IHNob3cgbGl0dGxlIHRvIG5vIG1vdXNlIGZyYWN0aW9uIGFzIHdvdWxkIGJlIGV4cGVjdGVkIChhIGdvb2Qgc2FuaXR5IGNoZWNrKS4KCk5vIGJpZyBpc3N1ZXMgLS0+IHByb2NlZWRpbmcgdG8gbWVyZ2UgbGFuZXMgYW5kIG1ldGEKCmBgYHtyfQpybmFzZXEgPC0gbWVyZ2UuUERYKHJuYXNlcSkKcm5hc2VxJG1ldGEgPC0gbWVyZ2Uocm5hc2VxJG1ldGEsbWV0YSxzb3J0PUYpCnJuYXNlcSRtZXRhWyxtZXJnZWQubGFuZXM6PWFiYnJldmlhdGUobWVyZ2VkLmxhbmVzKV0KYGBgCgojS2V5IHF1ZXN0aW9ucwoKKiBBcmUgdGhlcmUgYW55IGV4cHJlc3Npb24gZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgcGF0aWVudOKAmXMgcHJpbWFyeSBhbmQgbWV0IHNhbXBsZXM/CiogV2l0aGluIG1vZGVsIEFCODYxTS1YMVQsIGFyZSB0aGVyZSBhbnkgZXhwcmVzc2lvbiBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSB0dW1vdXIgYW5kIHRoZSBseW1waCBub2RlcyBtZXRzPwoqIFdpdGhpbiBtb2RlbCBBQjg2MU0tWDFULCBhcmUgdGhlcmUgYW55IGV4cHJlc3Npb24gZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgdHVtb3VyIGZyYWdtZW50cyB0aGF0IGRpZCBhbmQgZGlkIG5vdCBtZXRhc3Rhc2lzZT8gKHNlZSBBYmJ5KQoqIEFyZSB0aGVyZSBhbnkgZXhwcmVzc2lvbiBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSB0dW1vdXJzLCBhcyB0aGUgbW9kZWxzIGhhdmUgYmVlbiBwYXNzYWdlZCBhbmQgbWV0YXN0YXRpYyBwb3B1bGF0aW9ucyBoYXZlIGJlZW4gc2VsZWN0ZWQ/IEUuZy4gY29tcGFyZSB0dW1vdXJzIGZyb20gQUI4NjFNLVgxVCwgQUI4NjFYMU0tWDBULCBBQjg2MVgxTTEtWDBUCgojUENBcwoKQmVmb3JlIHdlIHRhY2tsZSB0aGUgYWJvdmUsIGxldCdzIHBlcmZvcm0gYSBmZXcgcXVpY2sgcGNhJ3MuCgpgYGB7cn0KcGNhIDwtIHBjYS5ydW4ocm5hc2VxJGh1bWFuKQpzdW1tYXJ5KHBjYSkKcGNhIDwtIGNiaW5kKHJuYXNlcSRtZXRhLHBjYSR4KQpnZ3Bsb3QocGNhKSArIGFlcyh4PVBDMSx5PVBDMixjb2w9dGlzc3VlLnNhbXBsZWQsc2hhcGU9eGVub2dyYWZ0KSArIGdlb21fcG9pbnQoc2l6ZT01KQpgYGAKClRoZSBodW1hbiBzYW1wbGVzIGFyZSBjbGVhcmx5IHZlcnkgZGlmZmVyZXQgdG8gdGhlIHJlc3QuIEhvd2V2ZXIsIHdoYXQgaXMgY2F1c2luZyB0aGUgZ3JvdXBpbmcgYmV0d2VlbiB0aGUgeGVub2dyYWZ0IHNhbXBsZXM/CgpgYGB7cn0KeCA8LSBzdWJzZXQuUERYKHJuYXNlcSxybmFzZXEkbWV0YVsseGVub2dyYWZ0XSkKcGNhIDwtIHBjYS5ydW4oeCRodW1hbikKc3VtbWFyeShwY2EpCnBjYSA8LSBjYmluZCh4JG1ldGEscGNhJHgpCmdncGxvdChwY2EpICsgYWVzKHg9UEMxLHk9UEMyLGNvbD10aXNzdWUuZ3JhZnRlZCxzaGFwZT1leHAucHJvdG9jb2wpICsgZ2VvbV9wb2ludChzaXplPTUpCmdncGxvdChwY2EpICsgYWVzKHg9UEMxLHk9UEMyLGNvbD10aXNzdWUuZ3JhZnRlZCxzaGFwZT10aXNzdWUuc2FtcGxlZCkgKyBnZW9tX3BvaW50KHNpemU9NSkKZ2dwbG90KHBjYSkgKyBhZXMoeD1QQzEseT1QQzIsY29sPXRpc3N1ZS5ncmFmdGVkLHNoYXBlPW1lcmdlZC5sYW5lcykgKyBnZW9tX3BvaW50KHNpemU9NSkKYGBgCgpXZSBub3RlIHRoYXQgdGhlcmUgaXMgY2x1c3RlcmluZyBiYXNlZCBvbiB0aGUgZW5ncmFmdGVkIHRpc3N1ZSBhbmQgdGhlIGV4cGVyaW1lbnRhbCBwcm90b2NvbC4gSG93ZXZlciwgc2luY2UgdGhlc2UgbGF0dGVyIGNsdXN0ZXJpbmdzIGFyZSBub3QgaW5kZXBlbmRlbnQgZnJvbSBvbmUgYW5vdGhlciwgd2UgY2Fubm90IGRpc2Nlcm4gaWYgdGhlIG1ham9yIHRyYW5zY3JpcHRvbWljIGNoYW5nZSBhZnRlciB0aGUgaW5pdGlhbCB0d28gcGFzc2FnZXMgaXMgY2F1c2VkIGJ5IGNoYW5nZXMgaW4gdGhlIHR1bW9yICh0aXNzdWUuZ3JhZnRlZCkgb3IgdGhlIGNoYW5nZWQgcHJvdG9jb2wuIEFkZGl0aW9uYWwgc2FtcGxlcyBzaG91bGQgYmUgc2VxdWVuY2VkIHRvIHRyeSBkaXNjZXJuIHRoaXMuIE5vdGUgdGhhdCB0aGlzIHBvaW50IGluIHRoZSBzZXF1ZW50aWFsIHBhc3NhZ2VzIGlzIHRoZSBmaXJzdCB0aW1lIGEgbW91c2UgbWV0IGlzIHJlaW1wbGFudGVkLgoKIyBNb2RlbDogfiAwICsgdGlzc3VlLmdyYWZ0ZWQgCgojIyBCZXR3ZWVuIGdlbmVyYXRpb25zIChNZXRzIG9ubHkpCgpgYGB7cn0KeCA8LSBzdWJzZXQuUERYKHJuYXNlcSxybmFzZXEkbWV0YVsseGVub2dyYWZ0ICYgISh0aXNzdWUuZ3JhZnRlZCAlaW4lIGMoIkFCODYxTSIsIkFCODYxTS1YVDEtWE0xLVhUMSIpKSAmIHRpc3N1ZS5zYW1wbGVkPT0iTE4iXSkKCnRpc3N1ZS5ncmFmdGVkIDwtIG1ha2UubmFtZXMoeCRtZXRhJHRpc3N1ZS5ncmFmdGVkKQoKZCA8LSBtb2RlbC5tYXRyaXgofiAwICsgdGlzc3VlLmdyYWZ0ZWQpCgpjb250Lm1hdHJpeCA8LSBtYWtlQ29udHJhc3RzKAogICBHMnZzRzEgPSB0aXNzdWUuZ3JhZnRlZEFCODYxTS5YVDEuWE0xIC0gdGlzc3VlLmdyYWZ0ZWRBQjg2MU0uWFQxLAogICBHM3ZzRzIgPSB0aXNzdWUuZ3JhZnRlZEFCODYxTS5YVDEuWE0xLlhNMSAtIHRpc3N1ZS5ncmFmdGVkQUI4NjFNLlhUMS5YTTEsCiAgIGxldmVscyA9IGNvbG5hbWVzKGQpKQoKI1BlcmZvcm0gREUKREUuZGF0YSA8LSBERS5ydW4oeCRodW1hbixkLDIsY29udC5tYXRyaXggPSBjb250Lm1hdHJpeCkKCkRFIDwtIGRlY2lkZVRlc3QuYW5ub3RhdGVkLkRFKERFLmRhdGEkZWZpdCkKdmVubkRpYWdyYW0ucGFpcmVkKERFWywyOjNdKQoKREUgPC0gREVbYWJzKHJvd1N1bXMoREVbLDI6M10pKT49Ml0KREUKCiNQaGVhdG1hcAppIDwtIG1hdGNoKERFJEVOU0VNQkwscm93bmFtZXMoREUuZGF0YSR2JEUpKQpsYWJlbHMuY29sIDwtIHJlcCgiIixucm93KHgkbWV0YSkpCmFubi5jb2wgPC0gZGF0YS5mcmFtZShHZW5lcmF0aW9uPXgkbWV0YVssdGlzc3VlLmdyYWZ0ZWRdKQpsYWJlbHMucm93IDwtIERFJFNZTUJPTApyb3duYW1lcyhhbm4uY29sKSA8LSB4JG1ldGFbLFNhbXBsZS5uYW1lXQpsZXZlbHMoYW5uLmNvbCRHZW5lcmF0aW9uKSA8LSBjKCJHMSIsIkcyIiwiRzMiKQpqIDwtIG9yZGVyKGFubi5jb2wkR2VuZXJhdGlvbikKY29scyA8LSBodWVfcGFsKCkoNClbMTozXQpuYW1lcyhjb2xzKSA8LSBsZXZlbHMoYW5uLmNvbCRHZW5lcmF0aW9uKQpwaGVhdG1hcChERS5kYXRhJHYkRVtpLGpdLGxhYmVsc19jb2w9bGFiZWxzLmNvbCxsYWJlbHNfcm93PWxhYmVscy5yb3csYW5ub3RhdGlvbl9jb2wgPSBhbm4uY29sW2osLGRyb3A9Rl0sc2NhbGU9InJvdyIsY2x1c3Rlcl9jb2xzID0gRixhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoR2VuZXJhdGlvbj1jb2xzKSkKCiNQaGVhdG1hcCB3aXRoIHRpc3N1ZS5ncmFmdGVkQUI4NjFNLlhUMS5YTTEuWFQxIGFkZGVkCnkgPC0gc3Vic2V0LlBEWChybmFzZXEscm5hc2VxJG1ldGFbLHhlbm9ncmFmdCAmICEodGlzc3VlLmdyYWZ0ZWQgJWluJSBjKCJBQjg2MU0iKSkgJiB0aXNzdWUuc2FtcGxlZD09IkxOIl0pCnogPC0gREdFTGlzdCh5JGh1bWFuW3Jvd1N1bXMoY3BtKHkkaHVtYW4pID4gY3BtLnRocmVzaG9sZCh5JGh1bWFuKSkgPj0gMixdKQp6IDwtIGNhbGNOb3JtRmFjdG9ycyh6LG1ldGhvZD0iVE1NIikKeiA8LSB2b29tKHoseSRtZXRhWyxtb2RlbC5tYXRyaXgofiAwICsgdGlzc3VlLmdyYWZ0ZWQpXSkKCmkgPC0gbWF0Y2goREUkRU5TRU1CTCxyb3duYW1lcyh6KSkKbGFiZWxzLmNvbCA8LSByZXAoIiIsbnJvdyh5JG1ldGEpKQphbm4uY29sIDwtIGRhdGEuZnJhbWUoR2VuZXJhdGlvbj15JG1ldGFbLHRpc3N1ZS5ncmFmdGVkXSkKbGFiZWxzLnJvdyA8LSBERSRTWU1CT0wKcm93bmFtZXMoYW5uLmNvbCkgPC0geSRtZXRhWyxTYW1wbGUubmFtZV0KbGV2ZWxzKGFubi5jb2wkR2VuZXJhdGlvbikgPC0gYygiRzEiLCJHMiIsIkczIiwiRzJ4MSIpCmNvbHMgPC0gaHVlX3BhbCgpKDQpCm5hbWVzKGNvbHMpIDwtIGxldmVscyhhbm4uY29sJEdlbmVyYXRpb24pCnBoZWF0bWFwKHpbaSxdLGxhYmVsc19jb2w9bGFiZWxzLmNvbCxsYWJlbHNfcm93PWxhYmVscy5yb3csYW5ub3RhdGlvbl9jb2wgPSBhbm4uY29sLHNjYWxlPSJyb3ciLGFubm90YXRpb25fY29sb3JzID0gbGlzdChHZW5lcmF0aW9uPWNvbHMpKQpgYGAKCiMjIEJldHdlZW4gZ2VuZXJhdGlvbnMgKEZsYW5rIG9ubHkpCgpgYGB7cn0KeCA8LSBzdWJzZXQuUERYKHJuYXNlcSxybmFzZXEkbWV0YVsseGVub2dyYWZ0ICYgISh0aXNzdWUuZ3JhZnRlZCAlaW4lIGMoIkFCODYxTSIsIkFCODYxTS1YVDEtWE0xLVhUMSIpKSAmIHRpc3N1ZS5zYW1wbGVkPT0iVCJdKQoKdGlzc3VlLmdyYWZ0ZWQgPC0gbWFrZS5uYW1lcyh4JG1ldGEkdGlzc3VlLmdyYWZ0ZWQpCgpkIDwtIG1vZGVsLm1hdHJpeCh+IDAgKyB0aXNzdWUuZ3JhZnRlZCkKCmNvbnQubWF0cml4IDwtIG1ha2VDb250cmFzdHMoCiAgIEcydnNHMSA9IHRpc3N1ZS5ncmFmdGVkQUI4NjFNLlhUMS5YTTEgLSB0aXNzdWUuZ3JhZnRlZEFCODYxTS5YVDEsCiAgIEczdnNHMiA9IHRpc3N1ZS5ncmFmdGVkQUI4NjFNLlhUMS5YTTEuWE0xIC0gdGlzc3VlLmdyYWZ0ZWRBQjg2MU0uWFQxLlhNMSwKICAgbGV2ZWxzID0gY29sbmFtZXMoZCkpCgojUGVyZm9ybSBERQpERS5kYXRhIDwtIERFLnJ1bih4JGh1bWFuLGQsMixjb250Lm1hdHJpeCA9IGNvbnQubWF0cml4KQoKREUgPC0gZGVjaWRlVGVzdC5hbm5vdGF0ZWQuREUoREUuZGF0YSRlZml0KQp2ZW5uRGlhZ3JhbS5wYWlyZWQoREVbLDI6M10pCgpERSA8LSBERVthYnMocm93U3VtcyhERVssMjozXSkpPT0yXQpERQoKI1BoZWF0bWFwCmkgPC0gbWF0Y2goREUkRU5TRU1CTCxyb3duYW1lcyhERS5kYXRhJHYkRSkpCmxhYmVscy5jb2wgPC0gcmVwKCIiLG5yb3coeCRtZXRhKSkKYW5uLmNvbCA8LSBkYXRhLmZyYW1lKEdlbmVyYXRpb249eCRtZXRhWyx0aXNzdWUuZ3JhZnRlZF0pCmxhYmVscy5yb3cgPC0gREUkU1lNQk9MCnJvd25hbWVzKGFubi5jb2wpIDwtIHgkbWV0YVssU2FtcGxlLm5hbWVdCmxldmVscyhhbm4uY29sJEdlbmVyYXRpb24pIDwtIGMoIkcxIiwiRzIiLCJHMyIpCmogPC0gb3JkZXIoYW5uLmNvbCRHZW5lcmF0aW9uKQpjb2xzIDwtIGh1ZV9wYWwoKSg0KVsxOjNdCm5hbWVzKGNvbHMpIDwtIGxldmVscyhhbm4uY29sJEdlbmVyYXRpb24pCnBoZWF0bWFwKERFLmRhdGEkdiRFW2ksal0sbGFiZWxzX2NvbD1sYWJlbHMuY29sLGxhYmVsc19yb3c9bGFiZWxzLnJvdyxhbm5vdGF0aW9uX2NvbCA9IGFubi5jb2xbaiwsZHJvcD1GXSxzY2FsZT0icm93IixjbHVzdGVyX2NvbHMgPSBGLGFubm90YXRpb25fY29sb3JzID0gbGlzdChHZW5lcmF0aW9uPWNvbHMpKQoKI1BoZWF0bWFwIHdpdGggdGlzc3VlLmdyYWZ0ZWRBQjg2MU0uWFQxLlhNMS5YVDEgYWRkZWQKeSA8LSBzdWJzZXQuUERYKHJuYXNlcSxybmFzZXEkbWV0YVsseGVub2dyYWZ0ICYgISh0aXNzdWUuZ3JhZnRlZCAlaW4lIGMoIkFCODYxTSIpKSAmIHRpc3N1ZS5zYW1wbGVkPT0iVCJdKQp6IDwtIERHRUxpc3QoeSRodW1hbltyb3dTdW1zKGNwbSh5JGh1bWFuKSA+IGNwbS50aHJlc2hvbGQoeSRodW1hbikpID49IDIsXSkKeiA8LSBjYWxjTm9ybUZhY3RvcnMoeixtZXRob2Q9IlRNTSIpCnogPC0gdm9vbSh6LHkkbWV0YVssbW9kZWwubWF0cml4KH4gMCArIHRpc3N1ZS5ncmFmdGVkKV0pCgppIDwtIG1hdGNoKERFJEVOU0VNQkwscm93bmFtZXMoeikpCmxhYmVscy5jb2wgPC0gcmVwKCIiLG5yb3coeSRtZXRhKSkKYW5uLmNvbCA8LSBkYXRhLmZyYW1lKEdlbmVyYXRpb249eSRtZXRhWyx0aXNzdWUuZ3JhZnRlZF0pCmxhYmVscy5yb3cgPC0gREUkU1lNQk9MCnJvd25hbWVzKGFubi5jb2wpIDwtIHkkbWV0YVssU2FtcGxlLm5hbWVdCmxldmVscyhhbm4uY29sJEdlbmVyYXRpb24pIDwtIGMoIkcxIiwiRzIiLCJHMyIsIkcyeDEiKQpjb2xzIDwtIGh1ZV9wYWwoKSg0KQpuYW1lcyhjb2xzKSA8LSBsZXZlbHMoYW5uLmNvbCRHZW5lcmF0aW9uKQpwaGVhdG1hcCh6W2ksXSxsYWJlbHNfY29sPWxhYmVscy5jb2wsbGFiZWxzX3Jvdz1sYWJlbHMucm93LGFubm90YXRpb25fY29sID0gYW5uLmNvbCxzY2FsZT0icm93Iixhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoR2VuZXJhdGlvbj1jb2xzKSkKYGBgCgoKIyBNb2RlbDogfiAwICsgdGlzc3VlLnNhbXBsZWQgKyB0aXNzdWUuZ3JhZnRlZCArIChiYXRjaCkKCiMjIEZsYW5rIHZzIEx5bXBoIAoKYGBge3J9CnggPC0gc3Vic2V0LlBEWChybmFzZXEscm5hc2VxJG1ldGFbLHhlbm9ncmFmdCAmIHRpc3N1ZS5ncmFmdGVkIT0iQUI4NjFNIl0pCgp0aXNzdWUuc2FtcGxlZCA8LSBmYWN0b3IobWFrZS5uYW1lcyh4JG1ldGEkdGlzc3VlLnNhbXBsZWQpLGxldmVscyA9IGMoIlQiLCJMTiIpKQp0aXNzdWUuZ3JhZnRlZCA8LSBtYWtlLm5hbWVzKHgkbWV0YSR0aXNzdWUuZ3JhZnRlZCkKbW91c2UuaWQgPC0gbWFrZS5uYW1lcyh4JG1ldGEkbW91c2UpCmV4cC5wcm90b2NvbCA8LSBtYWtlLm5hbWVzKHgkbWV0YSRleHAucHJvdG9jb2wpCm1lcmdlZC5sYW5lcyA8LSBtYWtlLm5hbWVzKHgkbWV0YSRtZXJnZWQubGFuZXMpCgpkIDwtIG1vZGVsLm1hdHJpeCh+IDAgKyB0aXNzdWUuc2FtcGxlZCArIHRpc3N1ZS5ncmFmdGVkICsgbW91c2UuaWQpCmQgPC0gZFssIShjb2xuYW1lcyhkKSAlaW4lIGMoIm1vdXNlLmlkWDQ1MjQxIiwibW91c2UuaWRYNDg4MDMiLCJtb3VzZS5pZFg0ODg5OCIpKV0KCmNvbnQubWF0cml4IDwtIG1ha2VDb250cmFzdHMoCiAgIExOdnNUID0gdGlzc3VlLnNhbXBsZWRMTiAtIHRpc3N1ZS5zYW1wbGVkVCwKICAgbGV2ZWxzID0gY29sbmFtZXMoZCkpCnJvd25hbWVzKGNvbnQubWF0cml4KSA8LSBjb2xuYW1lcyhkKQoKI1BlcmZvcm0gREUKREUuZGF0YSA8LSBERS5ydW4oeCRodW1hbixkLDIsY29udC5tYXRyaXggPSBjb250Lm1hdHJpeCkKCiNUb3AgREUgZ2VuZXMgdGFibGUKREUgPC0gdG9wVGFibGUuYW5ub3RhdGVkLkRFKERFLmRhdGEkZWZpdCxwLnZhbCA9IDAuMDEpCkRFIDwtIERFWyEoRU5TRU1CTCAlaW4lIGhwYS5nZW5lLmxpc3QkRW5zZW1ibCldCmlmKG5yb3coREUpIT0wKXsKICBwcmludChERVtkaXJlY3Rpb249PSJ1cC5yZWciLC4oZGlyZWN0aW9uLEVOU0VNQkwsU1lNQk9MLGFkai5QLlZhbCxsb2dGQyldKQogIHByaW50KERFW2RpcmVjdGlvbj09ImRvd24ucmVnIiwuKGRpcmVjdGlvbixFTlNFTUJMLFNZTUJPTCxhZGouUC5WYWwsbG9nRkMpXSkKCiNQaGVhdG1hcCB3LyBiYXRjaCBjb3JyZWN0aW9uCiAgaSA8LSBtYXRjaChERSRFTlNFTUJMLHJvd25hbWVzKERFLmRhdGEkdiRFKSkKICB5IDwtIHJlbW92ZUJhdGNoRWZmZWN0KERFLmRhdGEkdiRFW2ksXSxkZXNpZ24gPSBtb2RlbC5tYXRyaXgofnRpc3N1ZS5zYW1wbGVkKSwgYmF0Y2g9IHRpc3N1ZS5ncmFmdGVkKQogIGxhYmVscy5jb2wgPC0gcmVwKCIiLG5yb3coeCRtZXRhKSkKICBsYWJlbHMucm93IDwtIERFJFNZTUJPTAogIGFubi5jb2wgPC0gYXMuZGF0YS5mcmFtZSh4JG1ldGFbLC4odGlzc3VlLnNhbXBsZWQsdGlzc3VlLmdyYWZ0ZWQpXSkKICByb3duYW1lcyhhbm4uY29sKSA8LSB4JG1ldGFbLFNhbXBsZS5uYW1lXQogIHBoZWF0bWFwKHksbGFiZWxzX2NvbD1sYWJlbHMuY29sLGxhYmVsc19yb3c9bGFiZWxzLnJvdyxhbm5vdGF0aW9uX2NvbCA9IGFubi5jb2wsc2NhbGU9InJvdyIpCiAgCiAgI1BoZWF0bWFwIHcvbyBiYXRjaCBjb3JyZWN0aW9uCiAgaSA8LSBtYXRjaChERSRFTlNFTUJMLHJvd25hbWVzKERFLmRhdGEkdiRFKSkKICB5IDwtIERFLmRhdGEkdiRFW2ksXQogIGxhYmVscy5jb2wgPC0gcmVwKCIiLG5yb3coeCRtZXRhKSkKICBsYWJlbHMucm93IDwtIERFJFNZTUJPTAogIGFubi5jb2wgPC0gYXMuZGF0YS5mcmFtZSh4JG1ldGFbLC4odGlzc3VlLnNhbXBsZWQsdGlzc3VlLmdyYWZ0ZWQpXSkKICByb3duYW1lcyhhbm4uY29sKSA8LSB4JG1ldGFbLFNhbXBsZS5uYW1lXQogIHBoZWF0bWFwKHksbGFiZWxzX2NvbD1sYWJlbHMuY29sLGxhYmVsc19yb3c9bGFiZWxzLnJvdyxhbm5vdGF0aW9uX2NvbCA9IGFubi5jb2wsc2NhbGU9InJvdyIpCn0KCiNHU0VBIC0gYzYKZ3NlYSA8LSBHU0VBLnJ1bihERS5kYXRhJHYsZCxjb250Lm1hdHJpeCxIcy5jNiwwLjA1KQpnc2VhW0RpcmVjdGlvbj09IlVwIiwuKHJuLE5HZW5lcyxEaXJlY3Rpb24sRkRSKV0KZ3NlYVtEaXJlY3Rpb249PSJEb3duIiwuKHJuLE5HZW5lcyxEaXJlY3Rpb24sRkRSKV0KCiNHU0VBIC0gYzIKZ3NlYSA8LSBHU0VBLnJ1bihERS5kYXRhJHYsZCxjb250Lm1hdHJpeCxIcy5jMiwwLjA1KQpnc2VhW0RpcmVjdGlvbj09IlVwIiwuKHJuLE5HZW5lcyxEaXJlY3Rpb24sRkRSKV0KZ3NlYVtEaXJlY3Rpb249PSJEb3duIiwuKHJuLE5HZW5lcyxEaXJlY3Rpb24sRkRSKV0KCiMgc3NHU0VBCnNzR1NFQS5kYXRhIDwtIHNzR1NFQS5ydW4oREUuZGF0YSR2JEUsSHMuYzIsZCxjb250Lm1hdHJpeCA9IGNvbnQubWF0cml4KQoKI1RvcCBERSBnZW5lLnNldHMgdGFibGUKc3NHU0VBIDwtIHRvcFRhYmxlLmFubm90YXRlZC5zc0dTRUEoc3NHU0VBLmRhdGEkZWZpdCkKaWYobnJvdyhzc0dTRUEpIT0wKXsKICBwcmludChzc0dTRUFbZGlyZWN0aW9uPT0idXAucmVnIiwuKGRpcmVjdGlvbixnZW5lLnNldCxhZGouUC5WYWwsbG9nRkMpXSkKICBwcmludChzc0dTRUFbZGlyZWN0aW9uPT0iZG93bi5yZWciLC4oZGlyZWN0aW9uLGdlbmUuc2V0LGFkai5QLlZhbCxsb2dGQyldKQogIAogICNQaGVhdG1hcCB3LyBiYXRjaCBjb3JyZWN0aW9uCiAgaSA8LSBtYXRjaChzc0dTRUEkZ2VuZS5zZXQscm93bmFtZXMoc3NHU0VBLmRhdGEkdykpCiAgeSA8LSByZW1vdmVCYXRjaEVmZmVjdChzc0dTRUEuZGF0YSR3W2ksXSxkZXNpZ24gPSBtb2RlbC5tYXRyaXgofnRpc3N1ZS5zYW1wbGVkKSwgYmF0Y2g9IHRpc3N1ZS5ncmFmdGVkKQogIGxhYmVscy5jb2wgPC0gcmVwKCIiLG5yb3cocm5hc2VxJG1ldGEpKQogIGxhYmVscy5yb3cgPC0gc3NHU0VBJGdlbmUuc2V0CiAgYW5uLmNvbCA8LSBhcy5kYXRhLmZyYW1lKHJuYXNlcSRtZXRhWywuKHRpc3N1ZS5zYW1wbGVkLHRpc3N1ZS5ncmFmdGVkKV0pCiAgcm93bmFtZXMoYW5uLmNvbCkgPC0gcm5hc2VxJG1ldGFbLFNhbXBsZS5uYW1lXQogIHBoZWF0bWFwKHksbGFiZWxzX2NvbD1sYWJlbHMuY29sLGxhYmVsc19yb3c9bGFiZWxzLnJvdyxhbm5vdGF0aW9uX2NvbCA9IGFubi5jb2wsc2NhbGU9InJvdyIpCn0KYGBgCgojIyBCZXR3ZWVuIGdlbmVyYXRpb25zCgpgYGB7cn0KZCA8LSBtb2RlbC5tYXRyaXgofiAwICsgdGlzc3VlLmdyYWZ0ZWQgKyB0aXNzdWUuc2FtcGxlZCArIG1vdXNlLmlkKQpkIDwtIGRbLCEoY29sbmFtZXMoZCkgJWluJSBjKCJtb3VzZS5pZFg0NTI0MSIsIm1vdXNlLmlkWDQ4ODAzIiwibW91c2UuaWRYNDg4OTgiKSldCgpjb250Lm1hdHJpeCA8LSBtYWtlQ29udHJhc3RzKAogICBHMnZzRzEgPSB0aXNzdWUuZ3JhZnRlZEFCODYxTS5YVDEuWE0xIC0gdGlzc3VlLmdyYWZ0ZWRBQjg2MU0uWFQxLAogICBHM3ZzRzIgPSB0aXNzdWUuZ3JhZnRlZEFCODYxTS5YVDEuWE0xLlhNMSAtIHRpc3N1ZS5ncmFmdGVkQUI4NjFNLlhUMS5YTTEsCiAgIGxldmVscyA9IGNvbG5hbWVzKGQpKQpyb3duYW1lcyhjb250Lm1hdHJpeCkgPC0gY29sbmFtZXMoZCkKCnZmaXQgPC0gbG1GaXQoREUuZGF0YSR2LCBkKQp2Zml0IDwtIGNvbnRyYXN0cy5maXQodmZpdCwgY29udHJhc3RzID0gY29udC5tYXRyaXgpCmVmaXQgPC0gZUJheWVzKHZmaXQpCkRFLnN1YiA8LSBkZWNpZGVUZXN0LmFubm90YXRlZC5ERShlZml0KQoKdGFibGUoREUuc3ViWywyOjNdKQp2ZW5uRGlhZ3JhbS5wYWlyZWQoREUuc3ViWywyOjNdKQpERS5zdWJbYWJzKHJvd1N1bXMoREUuc3ViWywyOjNdKSk9PTIsXQpgYGAKCiMjIEJldHdlZW4gZ2VuZXJhdGlvbnMgKG9ubHkgRiB2cyBMTiBERSBnZW5lcykKCmBgYHtyfQppIDwtIG1hdGNoKERFJEVOU0VNQkwscm93bmFtZXMoREUuZGF0YSR2JEUpKQp2Zml0IDwtIGxtRml0KERFLmRhdGEkdltpLF0sIGQpCnZmaXQgPC0gY29udHJhc3RzLmZpdCh2Zml0LCBjb250cmFzdHMgPSBjb250Lm1hdHJpeCkKZWZpdCA8LSBlQmF5ZXModmZpdCkKREUuc3ViIDwtIGRlY2lkZVRlc3QuYW5ub3RhdGVkLkRFKGVmaXQpCgp0YWJsZShERS5zdWJbLDI6M10pCnZlbm5EaWFncmFtLnBhaXJlZChERS5zdWJbLDI6M10pCkRFLnN1YlthYnMocm93U3VtcyhERS5zdWJbLDI6M10pKT09MixdCmBgYAoKCiMjIEh1bWFuIGRhdGEKCmBgYHtyfQp5IDwtIHN1YnNldC5QRFgocm5hc2VxLHJuYXNlcSRtZXRhWyxpcy5uYSh0aXNzdWUuZ3JhZnRlZCldKSRodW1hbgp5IDwtIGxvZyh0KDFlNip0KHkpIC8gKGNvbFN1bXMoeSkqY2FsY05vcm1GYWN0b3JzKHkpKSswLjUpKQoKdXAucmVnIDwtIGFwcGx5KHlbcm93bmFtZXMoeSkgJWluJSBERVtkaXJlY3Rpb249PSJ1cC5yZWciLEVOU0VNQkxdLF0sMSxmdW5jdGlvbih5KXlbM10teVsyXSkKZG93bi5yZWcgPC0gYXBwbHkoeVtyb3duYW1lcyh5KSAlaW4lIERFW2RpcmVjdGlvbj09ImRvd24ucmVnIixFTlNFTUJMXSxdLDEsZnVuY3Rpb24oeSl5WzNdLXlbMl0pCgp3aWxjb3gudGVzdCh1cC5yZWcsZG93bi5yZWcsYWx0ZXJuYXRpdmUgPSAiZyIpCgp6IDwtIHJiaW5kKGRhdGEudGFibGUoZGlyZWN0aW9uPSJ1cCIsTEZDPXVwLnJlZyxFTlNFTUJMPW5hbWVzKHVwLnJlZykpLAogICAgICAgICAgIGRhdGEudGFibGUoZGlyZWN0aW9uPSJkb3duIixMRkM9ZG93bi5yZWcsRU5TRU1CTD1uYW1lcyhkb3duLnJlZykpKQpnZ3Bsb3QoeikgKyBhZXMoeD1MRkMsZmlsbD1kaXJlY3Rpb24pICsgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNSkKCnogPC0gbWVyZ2UoeixERSxieT0iRU5TRU1CTCIpCnRhYmxlKHpbLC4oZGlyZWN0aW9uLngsZGlyZWN0aW9uLmh1bWFuPUxGQz4wKV0pCnpbZGlyZWN0aW9uLng9PSJ1cCIsLihFTlNFTUJMLFNZTUJPTCxMRkMsZGlyZWN0aW9uLngsTEZDPjApXVtvcmRlcigtTEZDKV0KeltkaXJlY3Rpb24ueD09ImRvd24iLC4oRU5TRU1CTCxTWU1CT0wsTEZDLGRpcmVjdGlvbi54LExGQz4wKV1bb3JkZXIoTEZDKV0KYGBgCgojIE1vZGVsOiB+IDAgKyB0aXNzdWUuc2FtcGxlZCp0aXNzdWUuZ3JhZnRlZCArIChiYXRjaCkKCiMjIEZsYW5rIHZzIEx5bXBoCgpgYGB7cn0KeCA8LSBzdWJzZXQuUERYKHJuYXNlcSxybmFzZXEkbWV0YVsseGVub2dyYWZ0ICYgdGlzc3VlLmdyYWZ0ZWQhPSJBQjg2MU0iXSkKCnRpc3N1ZS5zYW1wbGVkIDwtIGZhY3RvcihtYWtlLm5hbWVzKHgkbWV0YSR0aXNzdWUuc2FtcGxlZCksbGV2ZWxzID0gYygiVCIsIkxOIikpCnRpc3N1ZS5ncmFmdGVkIDwtIG1ha2UubmFtZXMoeCRtZXRhJHRpc3N1ZS5ncmFmdGVkKQptb3VzZS5pZCA8LSBtYWtlLm5hbWVzKHgkbWV0YSRtb3VzZSkKZXhwLnByb3RvY29sIDwtIG1ha2UubmFtZXMoeCRtZXRhJGV4cC5wcm90b2NvbCkKbWVyZ2VkLmxhbmVzIDwtIG1ha2UubmFtZXMoeCRtZXRhJG1lcmdlZC5sYW5lcykKCmQgPC0gbW9kZWwubWF0cml4KH4gMCArIHRpc3N1ZS5zYW1wbGVkICogdGlzc3VlLmdyYWZ0ZWQgKyBtb3VzZS5pZCArIG1lcmdlZC5sYW5lcykKZCA8LSBkWywhKGNvbG5hbWVzKGQpICVpbiUgYygibW91c2UuaWRYNDUyNDEiLCJtb3VzZS5pZFg0ODgwMyIsIm1vdXNlLmlkWDQ4ODk4IikpXQoKY29udC5tYXRyaXggPC0gIG1hdHJpeChjKC0xLDEscmVwKDAsZGltKGQpWzJdLTIpKSkKcm93bmFtZXMoY29udC5tYXRyaXgpIDwtIGNvbG5hbWVzKGQpCgojUGVyZm9ybSBERQpERS5kYXRhIDwtIERFLnJ1bih4JGh1bWFuLGQsMixjb250Lm1hdHJpeCA9IGNvbnQubWF0cml4KQoKI1RvcCBERSBnZW5lcyB0YWJsZQpERSA8LSB0b3BUYWJsZS5hbm5vdGF0ZWQuREUoREUuZGF0YSRlZml0LHAudmFsID0gMC4wNSkKREUgPC0gREVbIShFTlNFTUJMICVpbiUgaHBhLmdlbmUubGlzdCRFbnNlbWJsKV0KaWYobnJvdyhERSkhPTApewogIHByaW50KERFW2RpcmVjdGlvbj09InVwLnJlZyIsLihkaXJlY3Rpb24sRU5TRU1CTCxTWU1CT0wsYWRqLlAuVmFsLGxvZ0ZDKV0pCiAgcHJpbnQoREVbZGlyZWN0aW9uPT0iZG93bi5yZWciLC4oZGlyZWN0aW9uLEVOU0VNQkwsU1lNQk9MLGFkai5QLlZhbCxsb2dGQyldKQoKI1BoZWF0bWFwIHcvIGJhdGNoIGNvcnJlY3Rpb24KICAjaSA8LSBtYXRjaChERSRFTlNFTUJMLHJvd25hbWVzKERFLmRhdGEkdiRFKSkKICAjeSA8LSByZW1vdmVCYXRjaEVmZmVjdChERS5kYXRhJHYkRVtpLF0sZGVzaWduID0gbW9kZWwubWF0cml4KH50aXNzdWUuc2FtcGxlZCksIGJhdGNoPSB0aXNzdWUuZ3JhZnRlZCkKICAjbGFiZWxzLnJvdyA8LSBERSRTWU1CT0wKICAjbGFiZWxzLmNvbCA8LSByZXAoIiIsbnJvdyh4JG1ldGEpKQogICNhbm4uY29sIDwtIGFzLmRhdGEuZnJhbWUoeCRtZXRhWywuKHRpc3N1ZS5zYW1wbGVkLHRpc3N1ZS5ncmFmdGVkKV0pCiAgI3Jvd25hbWVzKGFubi5jb2wpIDwtIHgkbWV0YVssU2FtcGxlLm5hbWVdCiAgI3BoZWF0bWFwKHksbGFiZWxzX2NvbD1sYWJlbHMuY29sLGxhYmVsc19yb3c9bGFiZWxzLnJvdyxhbm5vdGF0aW9uX2NvbCA9IGFubi5jb2wsc2NhbGU9InJvdyIpCn0KCiNHU0VBIC0gYzYKZ3NlYSA8LSBHU0VBLnJ1bihERS5kYXRhJHYsZCxjb250Lm1hdHJpeCxIcy5jNiwwLjA1KQpnc2VhW0RpcmVjdGlvbj09IlVwIiwuKHJuLE5HZW5lcyxEaXJlY3Rpb24sRkRSKV0KZ3NlYVtEaXJlY3Rpb249PSJEb3duIiwuKHJuLE5HZW5lcyxEaXJlY3Rpb24sRkRSKV0KCiNHU0VBIC0gYzIKZ3NlYSA8LSBHU0VBLnJ1bihERS5kYXRhJHYsZCxjb250Lm1hdHJpeCxIcy5jMiwwLjA1KQpnc2VhW0RpcmVjdGlvbj09IlVwIiwuKHJuLE5HZW5lcyxEaXJlY3Rpb24sRkRSKV0KZ3NlYVtEaXJlY3Rpb249PSJEb3duIiwuKHJuLE5HZW5lcyxEaXJlY3Rpb24sRkRSKV0KCiMgc3NHU0VBCnNzR1NFQS5kYXRhIDwtIHNzR1NFQS5ydW4oREUuZGF0YSR2JEUsSHMuYzIsZCxjb250Lm1hdHJpeCA9IGNvbnQubWF0cml4KQoKI1RvcCBERSBnZW5lLnNldHMgdGFibGUKc3NHU0VBIDwtIHRvcFRhYmxlLmFubm90YXRlZC5zc0dTRUEoc3NHU0VBLmRhdGEkZWZpdCkKaWYobnJvdyhzc0dTRUEpIT0wKXsKICBwcmludChzc0dTRUFbZGlyZWN0aW9uPT0idXAucmVnIiwuKGRpcmVjdGlvbixnZW5lLnNldCxhZGouUC5WYWwsbG9nRkMpXSkKICBwcmludChzc0dTRUFbZGlyZWN0aW9uPT0iZG93bi5yZWciLC4oZGlyZWN0aW9uLGdlbmUuc2V0LGFkai5QLlZhbCxsb2dGQyldKQogIAogICNQaGVhdG1hcCB3LyBiYXRjaCBjb3JyZWN0aW9uCiAgaSA8LSBtYXRjaChzc0dTRUEkZ2VuZS5zZXQscm93bmFtZXMoc3NHU0VBLmRhdGEkdykpCiAgeSA8LSByZW1vdmVCYXRjaEVmZmVjdChzc0dTRUEuZGF0YSR3W2ksXSxkZXNpZ24gPSBtb2RlbC5tYXRyaXgofnRpc3N1ZS5zYW1wbGVkKSwgYmF0Y2g9IHRpc3N1ZS5ncmFmdGVkKQogIGxhYmVscy5jb2wgPC0gcmVwKCIiLG5yb3cocm5hc2VxJG1ldGEpKQogIGxhYmVscy5yb3cgPC0gc3NHU0VBJGdlbmUuc2V0CiAgYW5uLmNvbCA8LSBhcy5kYXRhLmZyYW1lKHJuYXNlcSRtZXRhWywuKHRpc3N1ZS5zYW1wbGVkLHRpc3N1ZS5ncmFmdGVkKV0pCiAgcm93bmFtZXMoYW5uLmNvbCkgPC0gcm5hc2VxJG1ldGFbLFNhbXBsZS5uYW1lXQogIHBoZWF0bWFwKHksbGFiZWxzX2NvbD1sYWJlbHMuY29sLGxhYmVsc19yb3c9bGFiZWxzLnJvdyxhbm5vdGF0aW9uX2NvbCA9IGFubi5jb2wsc2NhbGU9InJvdyIpCn0KYGBgCgoKIyBNb2RlbDogfiB0aXNzdWUuc2FtcGxlZDp0aXNzdWUuZ3JhZnRlZCArIChiYXRjaCkKTm90ZTogb25seSB1c2luZyBwYWlyZWQgc2FtcGxlcwoKIyMgRmxhbmsgdnMgTHltcGgKCmBgYHtyfQp4IDwtIHN1YnNldC5QRFgocm5hc2VxLHJuYXNlcSRtZXRhWyx4ZW5vZ3JhZnQgJiBtb3VzZSAlaW4lIHJuYXNlcSRtZXRhWywuTixtb3VzZV1bTj09Mixtb3VzZV1dKQoKdGlzc3VlLnNhbXBsZWQgPC0gZmFjdG9yKG1ha2UubmFtZXMoeCRtZXRhJHRpc3N1ZS5zYW1wbGVkKSxsZXZlbHMgPSBjKCJUIiwiTE4iKSkKdGlzc3VlLmdyYWZ0ZWQgPC0gbWFrZS5uYW1lcyh4JG1ldGEkdGlzc3VlLmdyYWZ0ZWQpCm1vdXNlLmlkIDwtIG1ha2UubmFtZXMoeCRtZXRhJG1vdXNlKQpleHAucHJvdG9jb2wgPC0gbWFrZS5uYW1lcyh4JG1ldGEkZXhwLnByb3RvY29sKQptZXJnZWQubGFuZXMgPC0gbWFrZS5uYW1lcyh4JG1ldGEkbWVyZ2VkLmxhbmVzKQoKZCA8LSBtb2RlbC5tYXRyaXgofiB0aXNzdWUuc2FtcGxlZDp0aXNzdWUuZ3JhZnRlZCArIG1vdXNlLmlkKQpkIDwtIGRbLCEoY29sbmFtZXMoZCkgJWluJSBjKCJ0aXNzdWUuc2FtcGxlZFQ6dGlzc3VlLmdyYWZ0ZWRBQjg2MU0uWFQxIiwidGlzc3VlLnNhbXBsZWRUOnRpc3N1ZS5ncmFmdGVkQUI4NjFNLlhUMS5YTTEiLCJ0aXNzdWUuc2FtcGxlZFQ6dGlzc3VlLmdyYWZ0ZWRBQjg2MU0uWFQxLlhNMS5YTTEiLCJ0aXNzdWUuc2FtcGxlZFQ6dGlzc3VlLmdyYWZ0ZWRBQjg2MU0uWFQxLlhNMS5YVDEiKSldCgpERS5kYXRhIDwtIERFLnJ1bih4JGh1bWFuLGQsMixjb2VmcyA9IDEyOjE1KQoKREUgPC0gZGVjaWRlVGVzdC5hbm5vdGF0ZWQuREUoREUuZGF0YSRlZml0KQp2ZW5uRGlhZ3JhbS5wYWlyZWQoREVbLDI6NV0pCmBgYAoKCiNNaWNlIHdpdGggbWV0cyB2cyBtaWNlIHdpdGhvdXQgbWV0cyAoQUI4NjFNLVhUMSkKCiMjIERFOiBodW1hbiByZWFkcyAKCmBgYHtyfQp4IDwtIHN1YnNldC5QRFgocm5hc2VxLHJuYXNlcSRtZXRhWyx4ZW5vZ3JhZnQgJiB0aXNzdWUuZ3JhZnRlZD09IkFCODYxTS1YVDEiICYgdGlzc3VlLnNhbXBsZWQ9PSJUIl0pCgp4JG1ldGFbLG1ldHM6PSEobW91c2UgJWluJSBjKDM1MzA5LDM1MzExLDM1MzExLDM1MzEyLDM2MzYxKSldCm1ldHMgPC0gbWFrZS5uYW1lcyh4JG1ldGEkbWV0cykKZCA8LSBtb2RlbC5tYXRyaXgofiBtZXRzKQoKREUuZGF0YSA8LSBERS5ydW4oeCRodW1hbixkLDIsY29lZnMgPSAyKQoKI1RvcCBERSBnZW5lcyB0YWJsZQpERSA8LSB0b3BUYWJsZS5hbm5vdGF0ZWQuREUoREUuZGF0YSRlZml0LHAudmFsID0gMC4wNSkKaWYobnJvdyhERSkhPTApewogIERFIDwtIERFWyEoRU5TRU1CTCAlaW4lIGhwYS5nZW5lLmxpc3QkRW5zZW1ibCldCiAgcHJpbnQoREVbZGlyZWN0aW9uPT0idXAucmVnIiwuKGRpcmVjdGlvbixFTlNFTUJMLFNZTUJPTCxhZGouUC5WYWwsbG9nRkMpXSkKICBwcmludChERVtkaXJlY3Rpb249PSJkb3duLnJlZyIsLihkaXJlY3Rpb24sRU5TRU1CTCxTWU1CT0wsYWRqLlAuVmFsLGxvZ0ZDKV0pCgojUGhlYXRtYXAgdy8gYmF0Y2ggY29ycmVjdGlvbgogIGkgPC0gbWF0Y2goREUkRU5TRU1CTCxyb3duYW1lcyhERS5kYXRhJHYkRSkpCiAgeSA8LSByZW1vdmVCYXRjaEVmZmVjdChERS5kYXRhJHYkRVtpLF0sZGVzaWduID0gbW9kZWwubWF0cml4KH50aXNzdWUuc2FtcGxlZCksIGJhdGNoPSB0aXNzdWUuZ3JhZnRlZCkKICBsYWJlbHMucm93IDwtIERFJFNZTUJPTAogIGxhYmVscy5jb2wgPC0gcmVwKCIiLG5yb3coeCRtZXRhKSkKICBhbm4uY29sIDwtIGFzLmRhdGEuZnJhbWUoeCRtZXRhWywuKHRpc3N1ZS5zYW1wbGVkLHRpc3N1ZS5ncmFmdGVkKV0pCiAgcm93bmFtZXMoYW5uLmNvbCkgPC0geCRtZXRhWyxTYW1wbGUubmFtZV0KICBwaGVhdG1hcCh5LGxhYmVsc19jb2w9bGFiZWxzLmNvbCxsYWJlbHNfcm93PWxhYmVscy5yb3csYW5ub3RhdGlvbl9jb2wgPSBhbm4uY29sLHNjYWxlPSJyb3ciKQp9CgojR1NFQSAtIGM2CmdzZWEgPC0gR1NFQS5ydW4oREUuZGF0YSR2LGQsMixIcy5jNiwwLjA1KQpnc2VhW0RpcmVjdGlvbj09IlVwIiwuKHJuLE5HZW5lcyxEaXJlY3Rpb24sRkRSKV0KZ3NlYVtEaXJlY3Rpb249PSJEb3duIiwuKHJuLE5HZW5lcyxEaXJlY3Rpb24sRkRSKV0KCiNHU0VBIC0gYzIKZ3NlYSA8LSBHU0VBLnJ1bihERS5kYXRhJHYsZCwyLEhzLmMyLDAuMDUpCmdzZWFbRGlyZWN0aW9uPT0iVXAiLC4ocm4sTkdlbmVzLERpcmVjdGlvbixGRFIpXQpnc2VhW0RpcmVjdGlvbj09IkRvd24iLC4ocm4sTkdlbmVzLERpcmVjdGlvbixGRFIpXQoKIyBzc0dTRUEKc3NHU0VBLmRhdGEgPC0gc3NHU0VBLnJ1bihERS5kYXRhJHYkRSxIcy5jMixkLGNvZWZzID0gMikKCiNUb3AgREUgZ2VuZS5zZXRzIHRhYmxlCnNzR1NFQSA8LSB0b3BUYWJsZS5hbm5vdGF0ZWQuc3NHU0VBKHNzR1NFQS5kYXRhJGVmaXQpCmlmKG5yb3coc3NHU0VBKSE9MCl7CiAgcHJpbnQoc3NHU0VBW2RpcmVjdGlvbj09InVwLnJlZyIsLihkaXJlY3Rpb24sZ2VuZS5zZXQsYWRqLlAuVmFsLGxvZ0ZDKV0pCiAgcHJpbnQoc3NHU0VBW2RpcmVjdGlvbj09ImRvd24ucmVnIiwuKGRpcmVjdGlvbixnZW5lLnNldCxhZGouUC5WYWwsbG9nRkMpXSkKICAKICAjUGhlYXRtYXAgdy8gYmF0Y2ggY29ycmVjdGlvbgogIGkgPC0gbWF0Y2goc3NHU0VBJGdlbmUuc2V0LHJvd25hbWVzKHNzR1NFQS5kYXRhJHcpKQogIHkgPC0gcmVtb3ZlQmF0Y2hFZmZlY3Qoc3NHU0VBLmRhdGEkd1tpLF0sZGVzaWduID0gbW9kZWwubWF0cml4KH50aXNzdWUuc2FtcGxlZCksIGJhdGNoPSB0aXNzdWUuZ3JhZnRlZCkKICBsYWJlbHMuY29sIDwtIHJlcCgiIixucm93KHJuYXNlcSRtZXRhKSkKICBsYWJlbHMucm93IDwtIHNzR1NFQSRnZW5lLnNldAogIGFubi5jb2wgPC0gYXMuZGF0YS5mcmFtZShybmFzZXEkbWV0YVssLih0aXNzdWUuc2FtcGxlZCx0aXNzdWUuZ3JhZnRlZCldKQogIHJvd25hbWVzKGFubi5jb2wpIDwtIHJuYXNlcSRtZXRhWyxTYW1wbGUubmFtZV0KICBwaGVhdG1hcCh5LGxhYmVsc19jb2w9bGFiZWxzLmNvbCxsYWJlbHNfcm93PWxhYmVscy5yb3csYW5ub3RhdGlvbl9jb2wgPSBhbm4uY29sLHNjYWxlPSJyb3ciKQp9CgpgYGAKCiMjIERFOiBtb3VzZSByZWFkcyAKCmBgYHtyfQpERS5kYXRhIDwtIERFLnJ1bih4JG1vdXNlLGQsMixjb2VmcyA9IDIpCgojVG9wIERFIGdlbmVzIHRhYmxlCkRFIDwtIHRvcFRhYmxlLmFubm90YXRlZC5ERShERS5kYXRhJGVmaXQscC52YWwgPSAwLjA1KQppZihucm93KERFKSE9MCl7CiAgREUgPC0gREVbIShFTlNFTUJMICVpbiUgaHBhLmdlbmUubGlzdCRFbnNlbWJsKV0KICBwcmludChERVtkaXJlY3Rpb249PSJ1cC5yZWciLC4oZGlyZWN0aW9uLEVOU0VNQkwsU1lNQk9MLGFkai5QLlZhbCxsb2dGQyldKQogIHByaW50KERFW2RpcmVjdGlvbj09ImRvd24ucmVnIiwuKGRpcmVjdGlvbixFTlNFTUJMLFNZTUJPTCxhZGouUC5WYWwsbG9nRkMpXSkKfQpgYGAKCgojU2Vzc2lvbiBJbmZvCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGA=